﻿using System;
using System.Linq;
using System.Security.Cryptography;
using System.Web.Mvc;
using ChampionsLeagueBets.Domain;
using ChampionsLeagueBets.Domain.Common;
using ChampionsLeagueBets.Infrastructure;
using ChampionsLeagueBets.Web.Mvc.Authentication;
using ChampionsLeagueBets.Web.Mvc.Common;
using ChampionsLeagueBets.Web.Mvc.Controllers.ViewModels;
using SharpArch.NHibernate.Web.Mvc;
using MvcContrib;

namespace ChampionsLeagueBets.Web.Mvc.Controllers
{
	/// <summary>
	/// Kontroler zarzadzajacy uzytkownikami.
	/// </summary>
	public class UsersController : Controller
	{
		/// <summary>
		/// Dostep do bazy danych.
		/// </summary>
		private readonly IExRepository<User> _userRepository;

		/// <summary>
		/// Dostep do bazy danych.
		/// </summary>
		private readonly IExRepository<Email> _emailRepository;

		/// <summary>
		/// Konstruktor.
		/// </summary>
		/// <param name="userRepository"></param>
		/// <param name="emailRepository"></param>
		public UsersController(IExRepository<User> userRepository, IExRepository<Email> emailRepository)
		{
			_userRepository = userRepository;
			_emailRepository = emailRepository;
		}

		/// <summary>
		/// Lista uzytkownikow.
		/// </summary>
		/// <returns></returns>
		public ActionResult Index()
		{
			if (!RightPrincipal.CurrentUser.IsAdmin) return this.RedirectToAction<HomeController>(c => c.NoRights());

			var users = _userRepository.GetAll();
			return View(users);
		}

		/// <summary>
		/// Strona do tworzenia uzytkownika.
		/// </summary>
		/// <returns></returns>
		public ActionResult Create()
		{
			if (!RightPrincipal.CurrentUser.IsAdmin) return this.RedirectToAction<HomeController>(c => c.NoRights());

			return View();
		}

		/// <summary>
		/// Tworzenie nowego uzytkownika.
		/// </summary>
		/// <param name="userViewModel"></param>
		/// <returns></returns>
		[HttpPost]
		[ValidateAntiForgeryToken]
		[Transaction]
		public ActionResult Create(UserViewModel userViewModel)
		{
			if (!RightPrincipal.CurrentUser.IsAdmin) return this.RedirectToAction<HomeController>(c => c.NoRights());

			try
			{
				if (userViewModel.User.IsValid())
				{
					using (MD5 md5Hash = MD5.Create())
					{
						userViewModel.User.Password = Md5Helper.GetMd5Hash(md5Hash, userViewModel.FirstPassword);
					}
					_userRepository.SaveOrUpdate(userViewModel.User);
				}

				TempData[ActionConfirmation.TempDataKey] = ActionConfirmation.CreateSuccess("Poprawnie dodano gracza.");
				return this.RedirectToAction(c => c.Index());
			}
			catch
			{
				TempData[ActionConfirmation.TempDataKey] = ActionConfirmation.CreateError("Błąd w trakcie dodawania gracza.");
				return View();
			}
		}

		/// <summary>
		/// Strona do edycji danych uzytkownika.
		/// </summary>
		/// <param name="id"></param>
		/// <returns></returns>
		public ActionResult Edit(int id)
		{
			if (RightPrincipal.CurrentUser.Id == id || RightPrincipal.CurrentUser.IsAdmin)
			{
				UserViewModel userViewModel = new UserViewModel();
				userViewModel.User = _userRepository.Get(id);
				return View(userViewModel);
			}

			return this.RedirectToAction<HomeController>(c => c.NoRights());
		}

		/// <summary>
		/// Edycja danych uzytkownika.
		/// </summary>
		/// <param name="id"></param>
		/// <param name="userViewModel"></param>
		/// <returns></returns>
		[HttpPost]
		[ValidateAntiForgeryToken]
		[Transaction]
		public ActionResult Edit(int id, UserViewModel userViewModel)
		{
			if (RightPrincipal.CurrentUser.Id == id || RightPrincipal.CurrentUser.IsAdmin)
			{
				try
				{
					ViewData.ModelState.Remove("user.UserName");
					if (ViewData.ModelState.IsValid)
					{
						User user = _userRepository.Get(id);
						user.FirstName = userViewModel.User.FirstName;
						user.Surname = userViewModel.User.Surname;
						user.Email = userViewModel.User.Email;

						// zmiana wartosci jest mozliwa tylko jesli uzytkownik jest adminem.
						if (RightPrincipal.CurrentUser.IsAdmin)
						{
							user.IsAdmin = userViewModel.User.IsAdmin;
						}

						if (!string.IsNullOrEmpty(userViewModel.FirstPassword))
						{
							using (MD5 md5Hash = MD5.Create())
							{
								user.Password = Md5Helper.GetMd5Hash(md5Hash, userViewModel.FirstPassword);
							}
						}

						_userRepository.SaveOrUpdate(user);
					}

					TempData[ActionConfirmation.TempDataKey] = ActionConfirmation.CreateSuccess("Poprawnie zmieniono dane gracza.");

					if (RightPrincipal.CurrentUser.IsAdmin)
						return this.RedirectToAction(c => c.Index());

					return this.RedirectToAction<HomeController>(c => c.Index());
				}
				catch
				{
					TempData[ActionConfirmation.TempDataKey] = ActionConfirmation.CreateError("Błąd w trakcie zmiany danych gracza.");
					return View();
				}
			}

			return this.RedirectToAction<HomeController>(c => c.NoRights());
		}

		/// <summary>
		/// Usuwanie uzytkownika.
		/// </summary>
		/// <param name="id"></param>
		/// <returns></returns>
		[Transaction]
		public ActionResult Delete(int id)
		{
			if (!RightPrincipal.CurrentUser.IsAdmin) return this.RedirectToAction<HomeController>(c => c.NoRights());

			User user = _userRepository.Get(id);
			_userRepository.Delete(user);
			_userRepository.DbContext.CommitTransaction();

			//return this.RedirectToAction(c => c.Index());
			return PartialView("_UsersList", _userRepository.GetAll());
		}

		/// <summary>
		/// Strona do logowania do systemu.
		/// </summary>
		/// <returns></returns>
		public ActionResult LogOn()
		{
			return View();
		}

		/// <summary>
		/// Logowanie do systemu.
		/// </summary>
		/// <param name="userName"></param>
		/// <param name="password"></param>
		/// <returns></returns>
		[HttpPost]
		public ActionResult LogOn(string userName, string password)
		{
			try
			{
				string pass;
				using (MD5 md5Hash = MD5.Create())
				{
					pass = Md5Helper.GetMd5Hash(md5Hash, password);
				}

				var user = _userRepository.GetQuery().Where(u => u.UserName == userName && u.Password == pass).FirstOrDefault();

				if (user != null)
				{
					WebAuthentication.CreatePrincipal(user);

					return this.RedirectToAction<HomeController>(c => c.Index());
				}

				TempData[ActionConfirmation.TempDataKey] = ActionConfirmation.CreateError("Podałeś zły login lub hasło.");
				return View();
			}
			catch
			{
				TempData[ActionConfirmation.TempDataKey] = ActionConfirmation.CreateError("Wystąpił błąd podczas logowania.");
				return View();
			}
		}

		/// <summary>
		/// Użytkownik wylogowanie z systemu
		/// </summary>
		/// <returns>Okno logowania</returns>
		public ActionResult LogOut()
		{
			WebAuthentication.RemovePrincipal();
			return this.RedirectToAction<HomeController>(c => c.Index());
		}

		/// <summary>
		/// Sprawdzenie czy podany login jest dostepny.
		/// </summary>
		/// <param name="user"></param>
		/// <returns></returns>
		public JsonResult UserNameAvailable(User user)
		{
			if (user == null) throw new ArgumentNullException("user");

			// Pobieramy uzytkownika po loginie.
			User u = _userRepository.GetQuery().Where(l => l.UserName == user.UserName).FirstOrDefault();

			// Jesli nie ma uzytkownika lub jest to uzytkownik dla ktorego sprawdzamy.
			if (u == null || u.Id == user.Id)
			{
				return Json(true, JsonRequestBehavior.AllowGet);
			}
			return Json("Podany login jest zajęty", JsonRequestBehavior.AllowGet);
		}

		/// <summary>
		/// Strona do tworzenia uzytkownika.
		/// </summary>
		/// <returns></returns>
		public ActionResult ForgotPassword()
		{
			return View();
		}

		/// <summary>
		/// Strona do tworzenia uzytkownika.
		/// </summary>
		/// <returns></returns>
		[HttpPost]
		[Transaction]
		public ActionResult ForgotPassword(string userName)
		{
			User user = _userRepository.GetQuery().Where(u => u.UserName == userName).FirstOrDefault();

			//ActionConfirmation confirmation;
			if (user != null)
			{
				// Zapisanie guidu, dzieki ktoremu nastapi zmiana hasla.
				user.PasswordRecoveryGuid = Guid.NewGuid();

				Email email = new Email();
				email.ReceiverAddress = user.Email;
				email.ReceiverName = user.FullName;
				email.Subject = "[Bet-at-Work] Przywracanie hasła";
				email.Body = string.Format("<p>Witaj {0}.</p><p>Kliknij poniższy link, aby podać nowe hasło do systemu: " +
					"<a href='{3}/Users/ChangePassword?guid={1}'>{3}/Users/ChangePassword?guid={2}</a></p>" +
					"<p><font style='size:0.8em;color:gray;'>Poniższy email został wygenerowany automatycznie, prosimy na niego nie odpowiadać.</font></p>", user.FirstName, user.PasswordRecoveryGuid, user.PasswordRecoveryGuid, SettingManagementService.SystemAddress);


				_userRepository.SaveOrUpdate(user);

				// Fizyczne wyslanie wiadomosci.
				email = EmailManager.SendEmail(email);
				_emailRepository.SaveOrUpdate(email);

				TempData[ActionConfirmation.TempDataKey] = ActionConfirmation.CreateSuccess("Na podany przez Ciebie email została wysłana wiadomość z kolejnymi instrukcjami.");
			}
			else
			{
				TempData[ActionConfirmation.TempDataKey] = ActionConfirmation.CreateError("Użytkownik o podanym loginie nie istnieje.");
			}

			//return confirmation;
			return this.RedirectToAction(c => c.LogOn());
		}


		/// <summary>
		/// Użytkownik - wyswietlenie okna do zalogowania
		/// </summary>
		/// <returns>Widok okna logowania</returns>
		public ActionResult ChangePassword(string guid)
		{
			ViewData["guid"] = guid;
			return View();
		}

		/// <summary>
		/// Uzytkownik - zalogowanie do systemu
		/// </summary>
		/// <param name="guid">Guid dzięki, któemu możemy zidentyfikować użytkownika.</param>
		/// <param name="firstPassword"></param>
		/// <returns>Strona główna lub strona na którą próbował wejść użytkownik</returns>
		[HttpPost]
		[Transaction]
		[ValidateAntiForgeryToken]
		public ActionResult ChangePassword(string guid, string firstPassword)
		{
			//ActionConfirmation confirmation = userManagementService.ChangePassword(guid, firstPassword);
			User user = _userRepository.GetQuery().Where(u => u.PasswordRecoveryGuid.ToString() == guid).FirstOrDefault();

			if (user != null)
			{
				using (MD5 md5Hash = MD5.Create())
				{
					user.PasswordRecoveryGuid = null;
					user.Password = Md5Helper.GetMd5Hash(md5Hash, firstPassword);
				}
				_userRepository.SaveOrUpdate(user);
			}

			TempData[ActionConfirmation.TempDataKey] = ActionConfirmation.CreateSuccess("Poprawnie zmieniono hasło.");
			return this.RedirectToAction(c => c.LogOn());
		}
	}
}
